#include <nice2d/background.h>
#include <nice2d/application.h>
#include <nice2d/objectfactory.h>
#include <nice2d/deserializerfactory.h>
#include <nice2d/components/imagemanager.h>

IMPLEMENT_OBJECT(Background, Serializer)

class ColorBackground : public Background
{
    DECLARE_OBJECT(ColorBackground)
public:
    ColorBackground() = delete;
    ColorBackground(const sf::Color &inputColor):
        color(inputColor)
    {
    }
public:
    void applyBackground(sf::Shape &shape)override
    {
        shape.setFillColor(color);
    }

    PTree serialize() override
    {
        PTree item;
        item.add_child("color", serializeObject(color));
        return item;
    }

    void deserialize(const PTree &node) override
    {
        auto item = node.get_child_optional("color");
        if (item.has_value())
            color = deserializeColor(item.value());
    }
private:
    sf::Color color = sf::Color::Black;
};

IMPLEMENT_OBJECT(ColorBackground, Background)

class TextureBackground : public Background
{
    DECLARE_OBJECT(TextureBackground)
public:
    TextureBackground() = delete;
    TextureBackground(const std::string &inputFile)
    {
        file = inputFile;
    }
    TextureBackground(const std::string &inputFile, std::shared_ptr<sf::Texture> inputTexture,
                      bool inputSmooth,
                      const sf::IntRect &inputArea)
    {
        file = inputFile;
        texture = inputTexture;
        textureArea = inputArea;
        smooth = inputSmooth;
    }
public:
    void applyBackground(sf::Shape &shape)override
    {
        shape.setFillColor(sf::Color::White);
        shape.setTexture(texture.get());
    }

    PTree serialize() override
    {
        PTree node;
        node.add("file", file);
        node.add_child("textureArea", serializeObject(textureArea));
        node.add("smooth", smooth);
        return node;
    }

    void deserialize(const PTree &root) override
    {
        auto textureAreaNode = root.get_child_optional("textureArea");
        if (textureAreaNode.has_value())
            textureArea = deserializeIntRect(textureAreaNode.value());
        smooth = root.get<bool>("smooth");

        auto manager = ADD_COMPONENT(Application::getInstance(), ImageManager);
        texture = manager->loadTextureFromFile(file, textureArea);
        texture->setSmooth(smooth);
    }
private:
    std::string file;
    std::shared_ptr<sf::Texture> texture;
    sf::IntRect textureArea;
    bool smooth = false;
};

IMPLEMENT_OBJECT(TextureBackground, Background)

BackgroundPointer Background::create(const sf::Color &color)
{
    return std::make_shared<ColorBackground>(color);
}

BackgroundPointer Background::create(const std::string &file, bool smooth,
                                     const sf::IntRect &area)
{
    auto manager = ADD_COMPONENT(Application::getInstance(), ImageManager);
    auto texture = manager->loadTextureFromFile(file, area);
    if (!texture)
        return nullptr;
    return std::make_shared<TextureBackground>(file, texture, smooth, area);
}

class ColorBackgroundDeserializerFactory : public DeserializerFactory
{
public:
    std::shared_ptr<Serializer> createSerializer(const std::string &name,
                                                 const PTree &root) override
    {
        auto node = root.get_child_optional(std::string("color"));
        auto color = deserializeColor(node.value());
        return std::make_shared<ColorBackground>(color);
    }
};

REGISTER_OBJECT_WITH_TYPE(std::string, "ColorBackground", DeserializerFactory,
                          ColorBackgroundDeserializerFactory)

class TextureBackgroundDeserializerFactory : public DeserializerFactory
{
public:
    std::shared_ptr<Serializer> createSerializer(const std::string &name,
                                                 const PTree &node) override
    {
        auto file = node.get<std::string>(std::string("file"));
        auto product = std::make_shared<TextureBackground>(file);
        product->deserialize(node);
        return product;
    }
};

REGISTER_OBJECT_WITH_TYPE(std::string, "TextureBackground", DeserializerFactory,
                          TextureBackgroundDeserializerFactory)
